/*  extMDB 1.1 - an extension to gvSIG Desktop GIS to read GeoMedia MDB warehouses
 *  Copyright (C) 2008-2009  Stefano Orlando
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/*
 * Revision 1.1  02/05/09 Stefano Orlando
 * Revision 1.0  06/02/09 Stefano Orlando
 */
package com.iver.cit.gvsig.mdb.core;

import com.iver.cit.gvsig.fmap.core.FNullGeometry3D;
import com.iver.cit.gvsig.fmap.core.GeneralPathX;
import com.iver.cit.gvsig.fmap.core.IGeometry3D;
import com.iver.cit.gvsig.fmap.core.ShapeFactory;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
/**
 * Utilities for interfacing JTS and FMap 3D-Geometries.
 * @author Stefano Orlando
 */
public class MDBUtils {
	/**
	* Receives a JTS 3D-Geometry and returns a FMap 3D-IGeometry.
	* @param jts3DGeometry - the JTS 3D-Geometry to convert to 3D-IGeometry
	* @return 3D-IGeometry of FMap
	* @throws MDBConversionException
	* @author Stefano Orlando
	*/
	public final static IGeometry3D jts3D_to_IGeometry3D(Geometry jts3DGeometry)
		throws MDBConversionException {

		if (jts3DGeometry == null) {return new FNullGeometry3D();}
		
		if (jts3DGeometry instanceof Point) {
			Coordinate[] coord = jts3DGeometry.getCoordinates();
			return (IGeometry3D) ShapeFactory.createPoint3D(coord[0].x,coord[0].y, coord[0].z);
		}
		
		if (jts3DGeometry instanceof MultiPoint) {
			int numGeometries = ((MultiPoint)jts3DGeometry).getNumGeometries();
			double[] x = new double[numGeometries];
			double[] y = new double[numGeometries];
			double[] z = new double[numGeometries];
			for (int i = 0; i < numGeometries; i++) {
				x[i] = ((Point)((MultiPoint)jts3DGeometry).getGeometryN(i)).getCoordinate().x;
				y[i] = ((Point)((MultiPoint)jts3DGeometry).getGeometryN(i)).getCoordinate().y;
				z[i] = ((Point)((MultiPoint)jts3DGeometry).getGeometryN(i)).getCoordinate().z;
			}
			return (IGeometry3D) ShapeFactory.createMultipoint3D(x, y, z);			
		}
		
		if (jts3DGeometry instanceof LineString) {
			GeneralPathX gpx = new GeneralPathX();
			Coordinate[] coord = ((LineString)jts3DGeometry).getCoordinates();
			int numCoord = coord.length;
			double[] z = new double[numCoord];
			gpx.moveTo(coord[0].x, coord[0].y);
			z[0] = coord[0].z;
			for (int i = 1; i < numCoord; i++) {
				gpx.lineTo(coord[i].x, coord[i].y);
				z[i] = coord[i].z;
			}
			return (IGeometry3D) ShapeFactory.createPolyline3D(gpx, z);
		}
		
		if (jts3DGeometry instanceof MultiLineString) {
			GeneralPathX gpx = new GeneralPathX();
			int numGeometries = ((MultiLineString)jts3DGeometry).getNumGeometries();
			int numCoord = 0;
			for (int i = 0; i < numGeometries; i++) {
				numCoord += ((MultiLineString)jts3DGeometry).getGeometryN(i).getNumPoints();
			}
			double[] z = new double[numCoord];
			int index = 0;
			for (int i = 0; i < numGeometries; i++) {
				Coordinate[] coordN = ((MultiLineString)jts3DGeometry).getGeometryN(i).getCoordinates();
				int numCoordN = coordN.length;
				gpx.moveTo(coordN[0].x, coordN[0].y);
				z[index++] = coordN[0].z;
				for (int j = 1; j < numCoordN; j++) {
					gpx.lineTo(coordN[j].x, coordN[j].y);
					z[index++] = coordN[j].z;
				}
				coordN = null;
			}
			return (IGeometry3D) ShapeFactory.createPolyline3D(gpx, z);
		}
		
		if (jts3DGeometry instanceof Polygon) {
			GeneralPathX gpx = new GeneralPathX();
			
			LineString extLS = ((Polygon)jts3DGeometry).getExteriorRing();
			int numExtCoord = extLS.getNumPoints();
			int numInteriorRing = ((Polygon)jts3DGeometry).getNumInteriorRing();
			int numIntCoord = 0;
			for (int i = 0; i < numInteriorRing; i++) {
				numIntCoord += ((Polygon)jts3DGeometry).getInteriorRingN(i).getNumPoints();
			}
			double[] z = new double[numExtCoord + numIntCoord];
			
			Coordinate[] extCoord = extLS.getCoordinates();
			gpx.moveTo(extCoord[0].x, extCoord[0].y);
			z[0] = extCoord[0].z;
			int index = 1;
			for (int i = 1; i < numExtCoord; i++) {
				gpx.lineTo(extCoord[i].x, extCoord[i].y);
				z[index++] = extCoord[i].z;
			}
			
			if (numInteriorRing == 0) return (IGeometry3D) ShapeFactory.createPolygon3D(gpx, z);
			for (int j = 0; j < numInteriorRing; j++) {
				LineString intLSN = ((Polygon)jts3DGeometry).getInteriorRingN(j);
				int numIntCoordN = ((Polygon)jts3DGeometry).getInteriorRingN(j).getNumPoints();
				Coordinate[] intCoordN = intLSN.getCoordinates();
				gpx.moveTo(intCoordN[0].x, intCoordN[0].y);
				z[index++] = intCoordN[0].z;
				for (int k = 1; k < numIntCoordN; k++) {
					gpx.lineTo(intCoordN[k].x, intCoordN[k].y);
					z[index++] = intCoordN[k].z;
				}
				intLSN = null; intCoordN = null;
			}
			return (IGeometry3D) ShapeFactory.createPolygon3D(gpx, z);
		}

		if (jts3DGeometry instanceof MultiPolygon) {
			GeneralPathX gpx = new GeneralPathX();
			int numCoord = 0;
			for (int i = 0; i < ((MultiPolygon)jts3DGeometry).getNumGeometries(); i++) {
				numCoord += ((Polygon)jts3DGeometry.getGeometryN(i)).getNumPoints();
			}
			double[] z = new double[numCoord];
			int index = 0;
			for (int i = 0; i < ((MultiPolygon)jts3DGeometry).getNumGeometries(); i++) {
				Polygon p = (Polygon) jts3DGeometry.getGeometryN(i);
				gpx.append(jts3D_to_IGeometry3D(p), false);
				for (int j = 0; j < ((Polygon) jts3DGeometry.getGeometryN(i)).getNumPoints(); j++) {
					z[index++] = p.getCoordinates()[j].z;
				}
			}
			return (IGeometry3D) ShapeFactory.createPolygon3D(gpx, z);
		}
		// only GeometryCollections made of simple geometries (Point, LineString, Polygon) are supported
		if (jts3DGeometry instanceof GeometryCollection) {
			Class geometryCollectionSimpleType = getGeometryCollectionSimpleType((GeometryCollection)jts3DGeometry);
			if (geometryCollectionSimpleType == null) {
				throw new MDBConversionException("unsupported_geom_error");
			}
			GeometryFactory gf = new GeometryFactory();
			// MultiPoint
			if (geometryCollectionSimpleType == Point.class) {
				Coordinate[] coord = ((GeometryCollection)jts3DGeometry).getCoordinates();
				return jts3D_to_IGeometry3D(gf.createMultiPoint(coord));
			}
			// MultiLineString
			if (geometryCollectionSimpleType == LineString.class) {
				LineString[] l = new LineString[((GeometryCollection)jts3DGeometry).getNumGeometries()];
				for (int i = 0; i < ((GeometryCollection)jts3DGeometry).getNumGeometries(); i++) {
					l[i] = (LineString)((GeometryCollection)jts3DGeometry).getGeometryN(i);
				}
				return jts3D_to_IGeometry3D(gf.createMultiLineString(l));				
			}
			// MultiPolygon
			if (geometryCollectionSimpleType == Polygon.class) {
				Polygon[] p = new Polygon[((GeometryCollection)jts3DGeometry).getNumGeometries()];
				for (int i = 0; i < ((GeometryCollection)jts3DGeometry).getNumGeometries(); i++) {
					p[i] = (Polygon)((GeometryCollection)jts3DGeometry).getGeometryN(i);
				}
				return jts3D_to_IGeometry3D(gf.createMultiPolygon(p));
			}
		}
		return null;
	}
	/**
	* Checks if a JTS GeometryCollection is made by simple geometries (Point, LineString, Polygon).
	* @param gc - the JTS GeometryCollection to test
	* @return <code>Point.class</code>, <code>LineString.class</code> or <code>Polygon.class</code>
	* if the JTS GeometryCollection is made by corresponding simple geometries, <code>null</code> otherwise
	* @author Stefano Orlando
	*/	
	private static Class getGeometryCollectionSimpleType (GeometryCollection gc) {
		boolean isPoint = false;
		boolean isLineString = false;
		boolean isPolygon = false;
		for (int i = 0; i < gc.getNumGeometries(); i++) {
			Geometry geom = gc.getGeometryN(i);
			// not supported: Triangle and GeometryCollection composed by multi-geometries
			// or other geometry collections
			// LineSegment should be supported as LineString
			if (geom instanceof Point) isPoint = true;
			else if (geom instanceof LineString) isLineString = true;
			else if (geom instanceof Polygon) isPolygon = true;
		}
		if (isPoint && !isLineString && !isPolygon) return Point.class;
		else if (!isPoint && isLineString && !isPolygon) return LineString.class;
		else if (!isPoint && !isLineString && isPolygon) return Polygon.class;
		else return null;
	}
}
